#include "venus.h"
#include "error.h"

#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#include <algorithm>

#include "global_utils.h"

namespace venus {
#define VENUS_ERROR_IGNORE 0
#define VENUS_ERROR_CONTINUE 1
#define VENUS_ERROR_ABORT 2

const char *FindWordEnd(const char *buf) {
  while (*buf != '\0' && !isspace(*buf)) ++buf;
  return buf;
}

static void ProcessError(const char *format, va_list args, 
    const char *message, int disposition) {
#if !defined(VENUS_IS_WINDOWS)
  char *error_buf;
  if (vasprintf(&error_buf, format, args) == -1) {
    fprintf(stderr, "vasprintf() unable to allocate memory!\n");
    abort();
  }
#else
  char error_buf[2048];
  vsnprintf_s(error_buf, sizeof(error_buf), _TRUNCATE, format, args);
#endif
  if (disposition == VENUS_ERROR_IGNORE) {
    return;
  } else {
    int column = 0;
    int width = std::max(20, Utils::TerminalWidth() - 2);
    fputs(message, stderr);
    fputs(": ", stderr);
    column += strlen(message) + 2;
    const char *msg_pos = error_buf;
    while (true) {
      while (*msg_pos != '\0' && isspace(*msg_pos)) ++msg_pos;
      if (*msg_pos == '\0') break;
      const char *word_end = FindWordEnd(msg_pos);
      if (column + (word_end - msg_pos) > width)
        column = fprintf(stderr, "\n    ");
      while (msg_pos != word_end) {
        fputc(*msg_pos, stderr);
        ++column; ++msg_pos;
      }
      fputc(' ', stderr);
      ++column;
    }
    fputs("\n", stderr);
  }
  if (disposition == VENUS_ERROR_ABORT) {
#if defined(VENUS_IS_WINDOWS)
    __debugbreak();
#else
    abort();
#endif
  }
#if !defined(VENUS_IS_WINDOWS)
  free(error_buf);
#endif
}

void Info(const char *format, ...) {
  if (VenusOptions::quiet || !VenusOptions::verbose) return;
  va_list args;
  va_start(args, format);
  ProcessError(format, args, "Notice", VENUS_ERROR_CONTINUE);
  va_end(args);
}

void Warning(const char *format, ...) {
  if (VenusOptions::quiet) return;

  va_list args;
  va_start(args, format);
  ProcessError(format, args, "Warning", VENUS_ERROR_CONTINUE);
  va_end(args);
}

void Error(const char *format, ...) {
  //if (VenusOptions::quiet) return;

  va_list args;
  va_start(args, format);
  ProcessError(format, args, "Error", VENUS_ERROR_CONTINUE);
  va_end(args);
}

void Severe(const char *format, ...) {
  //if (VenusOptions::quiet) return;

  va_list args;
  va_start(args, format);
  ProcessError(format, args, "Fatal Error", VENUS_ERROR_ABORT);
  va_end(args);
}
} // namespace venus
